#ifndef _LOGGER_H_
#define _LOGGER_H_


#include "tools/defSingleton.h"
#include "tools/memChecker.h"

#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/type_traits/remove_const.hpp>

#include <string>
#include <time.h>
#include <ostream>
#include <map>
#include <list>
#include <iostream>
#include <sstream>


namespace sd
{
namespace tools
{
namespace logger
{

class  logManager : public defSingleton<logManager>
{
public:
	void init( std::wostream& defStream );
	void map( int section, std::wostream& stream );
	void log( const std::wstring& msg, int index=0 );
	
	logManager(): defStream_( 0 ){}

private:
	void workThread();


	typedef std::pair<int,std::wstring> LogMsg;
	typedef std::list< LogMsg > MsgList;
	typedef std::map< int, std::wostream* > Streams;

	MsgList list_;
	Streams streams_;
	std::wostream* defStream_;
	boost::thread thd_;
	boost::mutex mutex_;
};






template< typename THook >
class logger 
{
public:
	logger( const THook& hook, int index=0, std::wstring prefix =L"" ): originalCaller_( hook ),index_(index)
	{
		std::wstringstream stream;
		stream<<prefix<<hook.getName().c_str()<<"("<<hook.getLibrary().c_str()<<"):";
		msg_= stream.str();
	}

	inline typename THook::ResultType operator()( THook& hook, typename THook::ParamList& params )
	{ 
		std::wstringstream stream( msg_ );
		stream << "[" << clock() << "] " << msg_;
		ParseParams( stream, params );
		
		logManager::getInstance().log( stream.str(), index_ );

		return originalCaller_( hook, params );
	}
private:


	template< typename T >
	struct pushToStream
	{
		void operator()( std::wstringstream& msg, const T &val )
		{
			msg << val;
		}
	};

	template< typename T >
	struct Sizeof{ enum{ value = sizeof(T) }; };

	template<>
	struct Sizeof<void>{ enum{ value = 0 }; };

	template< typename T >
	struct pushToStream<T *>
	{
		void operator()( std::wstringstream& msg, const T* const &val )
		{
			if(!val)
				msg << "NULL";
			else
				msg << std::hex << L"0x" << val << std::dec;
		}
	};


	template<typename T>
	struct pushString
	{
		void operator()( std::wstringstream& msg, const T &val )
		{
			if(!val)
				msg << "NULL";
			else
				if( (int)val > 0x10000 &&  system::MemChecker::getInstance().read( val, sizeof(*val)*2 ) )
					msg << '"' << val << '"';
				else
					msg << std::hex << L"0x" << (int)val << std::dec;
		}
	};


	template<>
	struct pushToStream< char *> : public pushString<char*>{};

	template<>
	struct pushToStream< const char *> : public pushString<const char*>{};

	template< >
	struct pushToStream<wchar_t *> : public pushString<wchar_t*>{};

	template< >
	struct pushToStream<const wchar_t *> : public pushString<const wchar_t*>{};



	template <typename TupleTypeIterator, typename TupleTypeIteratorEnd >
	inline void LoadParam ( std::wstringstream& msg, const TupleTypeIterator & it, const TupleTypeIteratorEnd& it_end )
	{
		typedef typename boost::fusion::result_of::deref<TupleTypeIterator>::type ParamType;
		typedef typename boost::remove_reference< ParamType >::type ParamTypeNoRef;

		pushToStream<boost::remove_const< ParamTypeNoRef>::type> pusher;
		pusher(msg, *it);

		if(  boost::fusion::distance(it, it_end) > 1 )
			msg << ", ";

		LoadParam( msg, boost::fusion::next( it ), it_end );
	}


	template <typename TupleTypeIteratorEnd >
	inline void LoadParam ( std::wstringstream& msg, const TupleTypeIteratorEnd &, const TupleTypeIteratorEnd & ){}


	template <typename TupleType>
	inline void ParseParams (std::wstringstream& msg, const TupleType& params )
	{
		LoadParam( msg, boost::fusion::begin( params ), boost::fusion::end( params ) );
		msg << std::endl;
	}


	int index_;
	std::wstring msg_;
	typename THook::OriginalCaller originalCaller_;
};


template< typename THook >
logger<THook> makeLogger( const THook& hook, int index=0, std::wstring prefix =L"" )
{
	return logger<THook>( hook, index, prefix );
}



}//logger
}//tools
}//sd



#endif//_LOGGER_H_